home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / examples / misc / editor.pro next >
Text File  |  1997-07-08  |  27KB  |  831 lines

  1. ; $Id: editor.pro,v 1.13 1997/03/05 19:52:01 alan Exp $
  2. ;
  3. ; Copyright (c) 1995-1997, Research Systems, Inc.  All rights reserved.
  4. ;       Unauthorized reproduction prohibited.
  5. ;+
  6. ; NAME: Editor
  7. ;
  8. ; PURPOSE: Display an ASCII text file using widgets.
  9. ;
  10. ; MAJOR TOPICS: Text manipulation.
  11. ;
  12. ; CALLING SEQUENCE: Editor [, filename]
  13. ;
  14. ; INPUTS:
  15. ;     Filename:    A scalar string that contains the filename of the file
  16. ;        to display.  The filename can include a path to that file.
  17. ;               If the filename is omitted, the user will be prompted for
  18. ;               a filename, via the system function, DIALOG_PICKFILE().
  19. ;
  20. ; KEYWORD PARAMETERS:
  21. ;    FONT:   The name of the font to use.  If omitted use the default
  22. ;        font.
  23. ;
  24. ;    HEIGHT:    The number of text lines that the widget should display at one
  25. ;        time.  If this keyword is not specified, 24 lines is the 
  26. ;        default.
  27. ;
  28. ;    WIDTH:    The number of characters wide the widget should be.  If this
  29. ;        keyword is not specified, 80 characters is the default.
  30. ;
  31. ; PROCEDURE: Editor reads, writes and manipulates text strings ...
  32. ;
  33. ; MAJOR FUNCTIONS and PROCEDURES:
  34. ;
  35. ; COMMON BLOCKS and STRUCTURES:
  36. ;
  37. ; SIDE EFFECTS:
  38. ;    Triggers the XMANAGER if it is not already in use.
  39. ;
  40. ; MODIFICATION HISTORY:  Written by:  WSO, RSI, January 1995
  41. ;                        Modified by: ...
  42. ;-
  43.  
  44.  
  45. FUNCTION FindDelimiter, text, delimiters, column, row, delimiterIndex, $
  46.              endOfLineMarks, regionMarks, START_OF_LINE=startOfLine, $
  47.              END_OF_LINE=endOfLine
  48.  
  49.    IF (KEYWORD_SET(startOfLine) NE 0) AND (column EQ 0) THEN $
  50.       RETURN, 1
  51.  
  52.    IF (N_ELEMENTS(endOfLineMarks) EQ 0) THEN $
  53.       endOfLineMarks = ""
  54.  
  55.    IF (N_ELEMENTS(regionMarks) EQ 0) THEN $
  56.       regionMarks = ""
  57.  
  58.    delimiters = [delimiters, endOfLineMarks, regionMarks]
  59.  
  60.    WHILE 1 DO BEGIN
  61.  
  62.       lastColumn = STRLEN(text[row])-1
  63.  
  64.         ; Find the delimiter
  65.       WHILE (column LE lastColumn) DO BEGIN
  66.  
  67.          delimiterIndex = STRPOS(delimiters, STRMID(text[row], column, 1))
  68.  
  69.          delimiterGroup = (WHERE (delimiterIndex NE -1))[0]
  70.  
  71.          CASE delimiterGroup OF
  72.  
  73.               ; If delimiter found is a region marker (quote, double quote, etc)
  74.             2: BEGIN ; Skip to next region marker
  75.                currentColumn = column + 1
  76.                column = STRPOS(STRMID(text[row], currentColumn, lastColumn-column), $
  77.                        STRMID(delimiters[delimiterGroup], delimiterIndex[delimiterGroup], 1))
  78.                IF column EQ -1 THEN $
  79.                   column = lastColumn + 1 $
  80.                ELSE $
  81.                   column = column + currentColumn + 1
  82.             ENDCASE
  83.  
  84.               ; end of line mark found - force completion of for loop
  85.             1: column = lastColumn+1
  86.  
  87.               ; normal delimiter found
  88.             0: RETURN, 1
  89.  
  90.               ; Go to next column
  91.             ELSE: $
  92.                column = column + 1
  93.  
  94.          ENDCASE
  95.  
  96.       ENDWHILE
  97.  
  98.         ; We've reached the end of the current line
  99.  
  100.         ; If the keyword is set then return success
  101.       IF (KEYWORD_SET(endOfLine) NE 0) THEN $
  102.          RETURN, 1
  103.  
  104.         ; Go to the beginning of the next row of text
  105.       column = 0
  106.       row = row + 1
  107.  
  108.         ; Are we past the end of the last line of the text
  109.       IF ((SIZE(text))[0] EQ 0) OR (row GE (SIZE(text))[1]) THEN $
  110.          RETURN, 0
  111.  
  112.       IF (KEYWORD_SET(startOfLine) NE 0) THEN $
  113.          RETURN, 1
  114.  
  115.    ENDWHILE
  116.  
  117.      ; If the delimiters were not found in the string
  118.    RETURN, 0
  119.  
  120. END
  121.  
  122.  
  123. PRO GetNextWord, wTextEdit, SKIP_NUMBERS=skipNumbers
  124.  
  125.      ; Define a list of characters that make words in the text
  126.      ; Notice single quotes to avoid conflict with constant designaters
  127.    symbols = '0123456789'
  128.    alphabet = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  129.  
  130.    IF KEYWORD_SET(skipNumbers) NE 0 THEN $
  131.       wordCharacters = alphabet $
  132.    ELSE $
  133.       wordCharacters = alphabet + symbols
  134.  
  135.      ; Define a list of delimiters that separates words in the text
  136.    wordDelimiters = " (){}/@#&!?*%^~\|-+=<>.[]:,    "
  137.  
  138.      ; Define the end of line if it's not the carriage return
  139.    endOfLineMarks = ";$"
  140.  
  141.      ; Define region delimiters to ignore - in our case strings
  142.    regionMarks = "'" + '"'
  143.  
  144.      ; Define all delimiters that could spearate words
  145.    allDelimiters = [wordDelimiters, endOfLineMarks, regionMarks]
  146.  
  147.      ; Get the text from the text widget
  148.    WIDGET_CONTROL, wTextEdit, GET_VALUE=text
  149.  
  150.      ; Get the cursor position as a character offset from start of text
  151.    cursorOffsetPos = WIDGET_INFO(wTextEdit, /TEXT_SELECT)
  152.  
  153.      ; This is the position to start the word search
  154.    startPos = cursorOffsetPos[0] + cursorOffsetPos[1]
  155.  
  156.      ; initialize wordlength to zero length - in case no word was found
  157.    wordLength = 0
  158.  
  159.      ; Convert the character offset to a column/row position
  160.    columnRow = WIDGET_INFO(wTextEdit, TEXT_OFFSET_TO_XY=startPos)
  161.  
  162.    row = columnRow[1]
  163.    column = columnRow[0]
  164.  
  165.         ; Find the first delimiter
  166.    found = FindDelimiter(text, wordDelimiters, column, row, delimiterIndex, $
  167.              endOfLineMarks, regionMarks, /START_OF_LINE)
  168.  
  169.    IF found THEN BEGIN
  170.         ; Find the first alphanumeric character
  171.       found = FindDelimiter(text, wordCharacters, column, row, delimiterIndex, $
  172.              endOfLineMarks, regionMarks)
  173.  
  174.       IF found THEN BEGIN
  175.  
  176.          firstCharPos = [column, row]
  177.  
  178.            ; Find the second delimiter
  179.          found = FindDelimiter(text, wordDelimiters, column, row, delimiterIndex, $
  180.              endOfLineMarks, regionMarks, /END_OF_LINE)
  181.  
  182.          wordLength = column - firstCharPos[0]
  183.  
  184.          startPos = WIDGET_INFO(wTextEdit, TEXT_XY_TO_OFFSET=firstCharPos)
  185.  
  186.       ENDIF
  187.  
  188.    ENDIF
  189.  
  190.    WIDGET_CONTROL, wTextEdit, SET_TEXT_SELECT=[startPos, wordLength]
  191.  
  192.    WIDGET_CONTROL, wTextEdit, /INPUT_FOCUS
  193.  
  194. END
  195.  
  196.  
  197. PRO FindText, state, token
  198.  
  199.      ; Get the text from the text widget
  200.    WIDGET_CONTROL, state.wTextEdit, GET_VALUE=text
  201.  
  202.      ; First get the current cursor position and see if any tokens were
  203.      ; found after that position.  If not circle back to the beginning
  204.      ; of the text and continue searching from there.
  205.  
  206.      ; Get the cursor position as a character offset from start of text
  207.    cursorOffsetPos = WIDGET_INFO(state.wTextEdit, /TEXT_SELECT)
  208.  
  209.      ; Convert the character offset to a [column,row] position
  210.    cursorColumnRow = WIDGET_INFO(state.wTextEdit, $
  211.                  TEXT_OFFSET_TO_XY=cursorOffsetPos[0]+cursorOffsetPos[1])
  212.  
  213.      ; For readability only convert the cursorColumnRow array to a structure
  214.    current = {column:cursorColumnRow[0], row:cursorColumnRow[1]}
  215.  
  216.      ; Search for a token in the current row after the current column
  217.    column = STRPOS(text[current.row], token, current.column)
  218.  
  219.      ; If any tokens were found on the current line - 
  220.    IF column NE -1 THEN $
  221.         ; Save this [column,row] position as the location where the token was found
  222.       location = [column, current.row] $
  223.  
  224.    ELSE BEGIN
  225.  
  226.         ; The token wasn't found in the current row - therefore
  227.         ; search for the token from the next row to the last row
  228.       nextRow = current.row+1
  229.       lastRow = (SIZE(text))[1]-1
  230.  
  231.         ; If at the end of the text widget
  232.       IF nextRow GT lastRow THEN $
  233.          location = -1 $
  234.       ELSE $
  235.          location = STRPOS(text[nextRow:lastRow], token)
  236.  
  237.         ; row is equal to the first row indice where a token was found
  238.       row = (WHERE(location GT -1))[0]
  239.  
  240.         ; If any tokens were found - 
  241.       IF row NE -1 THEN $
  242.            ; Save the token location [column, row]
  243.          location = [location[row], row+current.row+1] $
  244.  
  245.       ELSE BEGIN
  246.  
  247.            ; The token wasn't found in the current row to the last row - therefore
  248.            ; search for the token from the first row to the current row
  249.          location = STRPOS(text[0:current.row], token)
  250.  
  251.            ; row is equal to the first row indice where a token was found
  252.          row = (WHERE(location GT -1))[0]
  253.  
  254.            ; If any tokens were found - 
  255.          IF row NE -1 THEN $
  256.               ; Save the token location [column, row]
  257.             location = [location[row], row] $
  258.          ELSE $
  259.             location = -1
  260.  
  261.       ENDELSE
  262.    ENDELSE
  263.  
  264.      ; If location[column] is valid - the token was found
  265.    IF location[0] GT -1 THEN BEGIN
  266.  
  267.         ; Get the character offset from the column, row position
  268.       startPos = WIDGET_INFO(state.wTextEdit, TEXT_XY_TO_OFFSET=location)
  269.  
  270.         ; Select the text that was found to match the search token
  271.       WIDGET_CONTROL, state.wTextEdit, SET_TEXT_SELECT=[startPos, STRLEN(token)]
  272.  
  273.         ; Enable the any controls that require text to be selected
  274.       TextSelected, state, 1
  275.  
  276.         ; Give the keyboard input focus back to the text widget
  277.       WIDGET_CONTROL, state.wTextEdit, /INPUT_FOCUS
  278.  
  279.    ENDIF ELSE $
  280.         ; Notify the user that the token was not found
  281.       button = DIALOG_MESSAGE(/INFO, 'The text "' + token + '" was not found.')
  282.  
  283. END
  284.  
  285.  
  286. FUNCTION CountWords, wTextEdit
  287.  
  288.    wordCount = 0
  289.  
  290.    GetNextWord, wTextEdit, /SKIP_NUMBERS
  291.  
  292.      ; Get selection to see if word was found 
  293.    cursorOffsetPos = WIDGET_INFO(wTextEdit, /TEXT_SELECT)
  294.  
  295.      ; While the word length is greater than zero - continue
  296.    WHILE cursorOffsetPos[1] GT 0 DO BEGIN
  297.  
  298.       wordCount = wordCount + 1
  299.  
  300.       GetNextWord, wTextEdit, /SKIP_NUMBERS
  301.  
  302.         ; Get selection to see if word was found 
  303.       cursorOffsetPos = WIDGET_INFO(wTextEdit, /TEXT_SELECT)
  304.  
  305.    ENDWHILE
  306.  
  307.    RETURN, wordCount
  308.  
  309. END
  310.  
  311.  
  312. PRO DisplayEditorInfo, parent
  313.  
  314.    infoText = [ $
  315.      "   Editor is an IDL example of a simple text "+ $
  316.      "editing application.  It demonstrates the ability to read and "+ $
  317.      "write text from a file and display it in a text widget.  It "+ $
  318.      "also provides a text search capability. ", "", $
  319.      '   Files can be opened and saved via commands in the "File" '+ $
  320.      "menu on the window's menubar.  The New command allows you "+ $
  321.      "to create a new text file. The Open menu command displays a "+ $
  322.      "standard file dialog to help you select a file to edit.  The "+ $
  323.      "Save and Save As menu commands write the text files back out "+ $
  324.      "to the disk.  The Exit Editor command will cause the Editor "+ $
  325.      "example to close and return to IDL.", "", $
  326.      "   To search for text in the text editor, enter the desired "+ $
  327.      "text into the Find Text field and click the Find button."]
  328.  
  329.    ShowInfo, TITLE="Editor Example Information", GROUP=parent, INFOTEXT=infoText, $
  330.      HEIGHT=15, WIDTH=80
  331.  
  332. END
  333.  
  334.  
  335. PRO EditorKilled, widgetID
  336.  
  337.    WIDGET_CONTROL, GET_UVALUE = state, widgetID, /NO_COPY
  338.  
  339.    ; If the Editor could be closed via the window manager, you would
  340.    ; need to add a request here for the user to save any changes they
  341.    ; might have made.  Currently the only way to close this editor 
  342.    ; is through the "Exit Editor" menu command.
  343.  
  344.    ; No need to reset the user value since it will no longer be used
  345. END
  346.  
  347.  
  348. PRO RequestToSave, state
  349.  
  350.      ; If the user editted (changed) the text in the text editor...
  351.    IF state.textEditted NE 0 THEN BEGIN
  352.  
  353.         ; See if they want to save any changes they made
  354.       buttonPushed = DIALOG_MESSAGE(/QUESTION, $
  355.                        ["Save changes to ", '"'+state.fileName+'"'])
  356.  
  357.         ; If the responded by clicking the "Yes" button ...
  358.       IF buttonPushed EQ "Yes" THEN BEGIN
  359.  
  360.            ; Save the text properly
  361.          IF state.fileName EQ "Untitled" THEN $
  362.             SaveAsText, state $
  363.          ELSE $
  364.             SaveText, state
  365.  
  366.       ENDIF
  367.    ENDIF
  368. END
  369.  
  370.  
  371. PRO GetText, state, FILENAME=fileName
  372.  
  373.      ; If any current text changed in the text editor -
  374.      ; request that the user save their changes first
  375.    RequestToSave, state
  376.  
  377.      ; If there isn't a filename defined - request one from the user
  378.    IF (NOT KEYWORD_SET(fileName)) THEN $
  379.       fileName = DIALOG_PICKFILE(GROUP=state.wEditorWindow)
  380.  
  381.      ; If the user didn't cancel the standard file dialog or
  382.      ; the filename already existed
  383.    IF STRLEN(fileName) GT 0 THEN BEGIN
  384.  
  385.         ; Save it in the state structure
  386.       state.fileName = fileName
  387.  
  388.         ; Set the title of the window to include the new file name
  389.       WIDGET_CONTROL, state.wEditorWindow, $
  390.         TLB_SET_TITLE="IDL Editor - " + state.fileName
  391.  
  392.         ; Display the wait cursor
  393.       WIDGET_CONTROL, state.wEditorWindow, /HOURGLASS
  394.  
  395.         ; Open the file to read
  396.       OPENR, unit, state.filename, /GET_LUN, ERROR=error
  397.  
  398.         ; If an error occurred
  399.       IF error LT 0 THEN $
  400.            ; Notify the User that an error occurred
  401.          buttonPushed = DIALOG_MESSAGE( [!err_string, $
  402.                           ' Cannot open the file ' + state.filename] ) $
  403.       ELSE BEGIN
  404.          maxLines = 1000
  405.          lineIncrement = 250
  406.          text = STRARR(maxLines)    ; Maximum # of lines
  407.          lineIndex = 0
  408.          lineOfText = ""
  409.          WHILE NOT EOF(unit) DO BEGIN
  410.             READF, unit, lineOfText
  411.             text[lineIndex] = lineOfText
  412.             lineIndex = lineIndex + 1
  413.               ; If the maximum number of lines is hit,
  414.               ; increase the array size by lineIncrement
  415.             IF (lineIndex EQ maxLines) THEN BEGIN
  416.                text = [text, STRARR(lineIncrement)]
  417.                maxLines = maxLines + lineIncrement
  418.             ENDIF
  419.  
  420.          ENDWHILE
  421.  
  422.          text = text[0:(lineIndex-1)>0]
  423.  
  424.          FREE_LUN, unit            ; Free and close the file unit.
  425.  
  426.            ; Initialize the flag to show the text is initially unchanged
  427.          state.textEditted = 0
  428.  
  429.            ; Insert the text into the text widget
  430.          WIDGET_CONTROL, state.wTextEdit, SET_VALUE=text
  431.  
  432.       ENDELSE
  433.  
  434.    ENDIF
  435.  
  436. END
  437.  
  438.  
  439. PRO SaveAsText, state
  440.  
  441.      ; Request a filename by which the file is to be saved
  442.    fileName = DIALOG_PICKFILE(/WRITE, GROUP=state.wEditorWindow)
  443.  
  444.      ; If the user didn't cancel the standard file dialog
  445.    IF STRLEN(fileName) GT 0 THEN BEGIN
  446.  
  447.         ; Update the window title to reflect the new filename
  448.       WIDGET_CONTROL, state.wEditorWindow, TLB_SET_TITLE="IDL Editor - " + fileName
  449.  
  450.         ; Store the filename in the application's state structure
  451.       state.fileName = fileName
  452.  
  453.         ; Write the text to disk
  454.       SaveText, state
  455.  
  456.    ENDIF
  457. END
  458.  
  459.  
  460. PRO SaveText, state
  461.  
  462.      ; Display the wait cursor
  463.    WIDGET_CONTROL, state.wEditorWindow, /HOURGLASS
  464.  
  465.      ; Open the file for writing
  466.    OPENW, unit, state.filename, /GET_LUN, ERROR=error
  467.  
  468.      ; If an error occurred - display an error message
  469.    IF error LT 0 THEN $
  470.       buttonPushed = DIALOG_MESSAGE([!err_string, $
  471.                        'Can not display the file:', state.filename] ) $
  472.    ELSE BEGIN
  473.         ; Get the text to save from the text widget
  474.       WIDGET_CONTROL, state.wTextEdit, GET_VALUE=text
  475.  
  476.         ; Count how many lines of text to save
  477.       lineCount = (SIZE(text))[1]
  478.  
  479.       FOR lineIndex = 0, lineCount-1 DO $
  480.  
  481.            ; Write each line to the file
  482.          PRINTF, unit, text[lineIndex]
  483.  
  484.       FREE_LUN, unit            ; Free and close the file unit.
  485.  
  486.         ; Reset the flag to show the text is saved
  487.       state.textEditted = 0
  488.  
  489.    ENDELSE
  490.  
  491. END
  492.  
  493.  
  494. PRO TextSelected, state, enabled
  495.  
  496.      ; Update these widgets when text is selected or deselected
  497.    WIDGET_CONTROL, state.wUppercase, SENSITIVE=enabled
  498.    WIDGET_CONTROL, state.wLowercase, SENSITIVE=enabled
  499.  
  500. END
  501.  
  502.  
  503. PRO EditorEventHdlr, event
  504.  
  505.      ; Get the state structure stored in the user value of the window
  506.    WIDGET_CONTROL, GET_UVALUE = state, event.top, /NO_COPY
  507.  
  508.      ; Determine in which widget the event occurred
  509.    CASE event.id OF
  510.       
  511.       state.wEditorWindow: BEGIN ; The window has been sized
  512.  
  513.            ; Get the new size of the window
  514.          WIDGET_CONTROL, state.wEditorWindow, TLB_GET_SIZE=windowSize
  515.         
  516.            ; Determine the change in the window size
  517.          deltaX = windowSize[0] - state.windowSize[0]
  518.          deltaY = windowSize[1] - state.windowSize[1]
  519.  
  520.            ; Get the pixel size of the text widget
  521.          textEditGeometry = WIDGET_INFO(state.wTextEdit, /GEOMETRY)
  522.  
  523.            ; Determine the new size based on the amount the window grew
  524.          newTextEditXSize = textEditGeometry.scr_xsize + deltaX
  525.          newTextEditYSize = textEditGeometry.scr_ysize + deltaY
  526.  
  527.            ; Resize the text widget accordingly
  528.          WIDGET_CONTROL, state.wTextEdit, SCR_XSIZE=newTextEditXSize, $
  529.            SCR_YSIZE=newTextEditYSize
  530.  
  531.           ; Store the new size in the state structure for later comparisons
  532.          WIDGET_CONTROL, state.wEditorWindow, TLB_GET_SIZE=windowSize
  533.          state.windowSize = windowSize
  534.  
  535.       ENDCASE
  536.  
  537.       state.wNewButton: BEGIN
  538.  
  539.            ; If any text changed in the text editor -
  540.            ; request that the user save the changes first
  541.          RequestToSave, state
  542.  
  543.            ; Set the default filename to "Untitled"
  544.          state.fileName = "Untitled"
  545.  
  546.            ; Initialize the flag to show the text is initially unchanged
  547.          state.textEditted = 0
  548.  
  549.            ; Clear any text currently in the text widget
  550.          WIDGET_CONTROL, state.wTextEdit, SET_VALUE=""
  551.  
  552.            ; Set the title of the window
  553.          WIDGET_CONTROL, event.top, $
  554.            TLB_SET_TITLE="IDL Editor - " + state.fileName
  555.  
  556.       ENDCASE
  557.          
  558.       state.wOpenButton: $
  559.  
  560.            ; Get the text from the file and insert it into the text widget
  561.          GetText, state
  562.          
  563.       state.wSaveButton: BEGIN
  564.  
  565.            ; If a new file was created - first request a filename and save
  566.          IF state.fileName EQ "Untitled" THEN $
  567.             SaveAsText, state $
  568.          ELSE $
  569.               ; Save the text to disk
  570.             SaveText, state
  571.  
  572.       ENDCASE
  573.  
  574.       state.wSaveAsButton: $
  575.            ; Request a filename and save the file to disk
  576.          SaveAsText, state
  577.          
  578.       state.wExitButton: BEGIN
  579.            ; If any text changed in the text editor -
  580.            ; request that the user save the changes first
  581.          RequestToSave, state
  582.  
  583.            ; Restore the state value before the widget app is destroyed
  584.            ; so the KILL_NOTIFY procedure can still use it
  585.          WIDGET_CONTROL, SET_UVALUE = state, event.top, /NO_COPY
  586.  
  587.            ; Exit the IDL Editor widget application
  588.          WIDGET_CONTROL, event.top, /DESTROY
  589.  
  590.          RETURN
  591.  
  592.       ENDCASE
  593.          
  594.       state.wTextEdit: BEGIN
  595.  
  596.            ; If text has been selected enable the appropriate widgets
  597.            ; initially assume that no text is selected
  598.          selected = 0
  599.  
  600.            ; If the user selected some text
  601.          IF event.type EQ 3 THEN BEGIN
  602.  
  603.             IF event.length GT 0 THEN $
  604.                selected = 1  ; Enable the upper and lower case pushbuttons
  605.  
  606.          ENDIF ELSE $
  607.               ; The user entered or deleted text in the text widget
  608.               ; therefore flag the text as changed to force SAVE request
  609.             state.textEditted = 1
  610.  
  611.          TextSelected, state, selected
  612.  
  613.       ENDCASE
  614.  
  615.       state.wFindTextID: BEGIN
  616.  
  617.            ; Get the text to search for from the Find Text widget
  618.          WIDGET_CONTROL, event.id, GET_VALUE=findText
  619.  
  620.            ; If the user has entered text into the Find Text field -
  621.          IF STRLEN(findText[0]) GT 0 THEN $
  622.             WIDGET_CONTROL, state.wFind, /SENSITIVE $  ; enable the Find button
  623.          ELSE $
  624.             WIDGET_CONTROL, state.wFind, SENSITIVE=0  ; disable the Find button
  625.  
  626.       ENDCASE
  627.  
  628.       state.wFind: BEGIN
  629.  
  630.            ; Get the text to search for from the Find Text widget
  631.          WIDGET_CONTROL, state.wFindTextID, GET_VALUE=findText
  632.  
  633.            ; If there is at least one character ...
  634.          IF STRLEN(findText[0]) GT 0 THEN $
  635.               ; Search for the text in the editor
  636.             FindText, state, findText[0]
  637.  
  638.       ENDCASE
  639.          
  640.       state.wUppercase: BEGIN
  641.  
  642.            ; Get the selected text in the editor
  643.          WIDGET_CONTROL, state.wTextEdit, GET_VALUE=selection, /USE_TEXT_SELECT
  644.  
  645.            ; If there is at least one character selected...
  646.          IF STRLEN(selection[0]) GT 0 THEN BEGIN
  647.  
  648.               ; Convert the selection to uppercase 
  649.             selection = STRUPCASE(selection)
  650.  
  651.               ; Set the selection to the uppercase conversion
  652.             WIDGET_CONTROL, state.wTextEdit, SET_VALUE=selection, $
  653.               /USE_TEXT_SELECT, /NO_NEWLINE
  654.  
  655.          ENDIF
  656.       ENDCASE
  657.          
  658.       state.wLowercase: BEGIN
  659.  
  660.            ; Get the selected text in the editor
  661.          WIDGET_CONTROL, state.wTextEdit, GET_VALUE=selection, /USE_TEXT_SELECT
  662.  
  663.            ; If there is at least one character selected...
  664.          IF STRLEN(selection[0]) GT 0 THEN BEGIN
  665.  
  666.               ; Convert the selection to lowercase 
  667.             selection = STRLOWCASE(selection)
  668.  
  669.               ; Set the selection to the lowercase conversion
  670.             WIDGET_CONTROL, state.wTextEdit, SET_VALUE=selection, $
  671.               /USE_TEXT_SELECT, /NO_NEWLINE
  672.  
  673.          ENDIF
  674.       ENDCASE
  675.          
  676.       state.wNextWord: BEGIN
  677.          GetNextWord, state.wTextEdit, /SKIP_NUMBERS
  678.  
  679.            ; Get selection to see if word was found 
  680.          cursorOffsetPos = WIDGET_INFO(state.wTextEdit, /TEXT_SELECT)
  681.  
  682.          enabled = (cursorOffsetPos[1] GT 0)
  683.  
  684.          WIDGET_CONTROL, state.wUppercase, SENSITIVE=enabled
  685.          WIDGET_CONTROL, state.wLowercase, SENSITIVE=enabled
  686.  
  687.       ENDCASE
  688.  
  689.       state.wCountWords: BEGIN
  690.          start = SYSTIME(1)
  691.          wordCount = CountWords( state.wTextEdit )
  692.          displayText = 'The number of words in "' + state.fileName + $
  693.                         '" is ' + STRING(wordCount, FORMAT='(I0)')
  694.          buttonPushed = DIALOG_MESSAGE(/INFORMATION, displayText)
  695.       ENDCASE
  696.  
  697.       state.wInfo: $ ; Display information about the IDL Editor
  698.          DisplayEditorInfo, event.top
  699.  
  700.       ELSE: $ ; We erroneously received an event for a widget we weren't expecting
  701.          buttonPushed = DIALOG_MESSAGE("An event occurred for a non-existent widget")
  702.  
  703.       ENDCASE
  704.  
  705.      ; Reset the windows user value to the updated state structure
  706.    WIDGET_CONTROL, SET_UVALUE = state, event.top, /NO_COPY
  707.  
  708. END
  709.  
  710.  
  711. PRO Editor, fileName, WIDTH = WIDTH, HEIGHT = HEIGHT, FONT = font, GROUP = group
  712.  
  713.      ; If keywords not set - set to defaults
  714.    IF (NOT(KEYWORD_SET(height))) THEN $
  715.       height = 24
  716.    IF(NOT(KEYWORD_SET(width))) THEN $
  717.       width = 80
  718.  
  719.      ; Create the text editor window with a menu bar
  720.    wEditorWindow = WIDGET_BASE(TITLE = "IDL Editor - Untitled", $
  721.                    MBAR=menuBar, /COLUMN, /TLB_SIZE_EVENTS, $
  722.                    TLB_FRAME_ATTR=8)
  723.  
  724.      ; Build the menubar
  725.    wFileMenu = WIDGET_BUTTON(menuBar, VALUE="File", /MENU)
  726.  
  727.      ; Add the following menu items to the window's File menu
  728.    wNewButton = WIDGET_BUTTON(wFileMenu, VALUE="New")
  729.    wOpenButton = WIDGET_BUTTON(wFileMenu, VALUE="Open...")
  730.    wSaveButton = WIDGET_BUTTON(wFileMenu, /SEPARATOR, VALUE="Save")
  731.    wSaveAsButton = WIDGET_BUTTON(wFileMenu, VALUE="Save As...")
  732.    wExitButton = WIDGET_BUTTON(wFileMenu, /SEPARATOR, VALUE="Exit Editor")
  733.    
  734.      ; If the FONT keyword was defined with a font
  735.    IF N_ELEMENTS(font) GT 0 THEN $
  736.         ; Create the text editor's text widget with the specified font
  737.       wTextEdit = WIDGET_TEXT(wEditorWindow, XSIZE=WIDTH, YSIZE=HEIGHT, $
  738.                    /SCROLL, FONT=font, /EDITABLE, /ALL_EVENTS) $
  739.    ELSE $
  740.         ; Create the text editor's text widget with the default font
  741.       wTextEdit = WIDGET_TEXT(wEditorWindow, XSIZE=WIDTH, YSIZE=HEIGHT, $
  742.                    /SCROLL, /EDITABLE, /ALL_EVENTS)
  743.  
  744.      ; Create a base for all the edit controls (button, etc.)
  745.    wControlBase = WIDGET_BASE(wEditorWindow, /COLUMN)
  746.    
  747.    wTopBase = WIDGET_BASE(wControlBase, SPACE=20, /ROW)
  748.    
  749.    wFindBase = WIDGET_BASE(wTopBase, /ROW)
  750.    
  751.      ; Create a label for the search text field
  752.    wFindLabel = WIDGET_LABEL(wFindBase, VALUE="Find Text:")
  753.  
  754.      ; Create the search text field
  755.    wFindTextID = WIDGET_TEXT(wFindBase, XSIZE=24, YSIZE=1, $
  756.                    /EDITABLE, /ALL_EVENTS)
  757.    
  758.      ; Create the search text pushbutton
  759.    wFind = WIDGET_BUTTON(wFindBase, VALUE = "Find")
  760.  
  761.      ; Create a pushbutton to display information about this application
  762.    wInfo = WIDGET_BUTTON(wTopBase, VALUE = "Info...")
  763.  
  764.  
  765.    wBottomBase = WIDGET_BASE(wControlBase, /ROW, SPACE=60)
  766.    
  767.    wCaseBase = WIDGET_BASE(wBottomBase, /ROW)
  768.    
  769.    wUppercase = WIDGET_BUTTON(wCaseBase, VALUE = "Uppercase")
  770.    
  771.    wLowercase = WIDGET_BUTTON(wCaseBase, VALUE = "Lowercase")
  772.  
  773.    wWordBase = WIDGET_BASE(wBottomBase, /ROW)
  774.    
  775.    wNextWord = WIDGET_BUTTON(wWordBase, VALUE = "Next Word")
  776.  
  777.    wCountWords = WIDGET_BUTTON(wWordBase, VALUE = "Count Words")
  778.  
  779.      ; Initially disable the pushbuttons, until the required user event
  780.    WIDGET_CONTROL, wUppercase, SENSITIVE=0
  781.    WIDGET_CONTROL, wLowercase, SENSITIVE=0
  782.    WIDGET_CONTROL, wFind, SENSITIVE=0
  783.  
  784.      ; Save the widget ids and other parameters to be accessed throughout 
  785.      ; this widget application.  This state structure will be stored
  786.      ; in the user value of the window and can be retreived through the 
  787.      ; GET_UVALUE keyword of the IDL WIDGET_CONTROL procedure
  788.    state = { $
  789.              wEditorWindow : wEditorWindow, $
  790.              wNewButton : wNewButton, $
  791.              wOpenButton : wOpenButton, $
  792.              wSaveButton : wSaveButton, $
  793.              wSaveAsButton : wSaveAsButton, $
  794.              wExitButton : wExitButton, $
  795.              wTextEdit : wTextEdit, $
  796.              wUppercase : wUppercase, $
  797.              wLowercase : wLowercase, $
  798.              wNextWord : wNextWord, $
  799.              wCountWords : wCountWords, $
  800.              wFindTextID: wFindTextID, $
  801.              wFind : wFind, $
  802.              wInfo : wInfo, $
  803.              windowSize : [0,0], $
  804.              textEditted : 0, $
  805.              fileName : "Untitled" $
  806.            }
  807.  
  808.      ; Make the window visible
  809.    WIDGET_CONTROL, wEditorWindow, /REALIZE
  810.    
  811.      ; Display the wait cursor
  812.    WIDGET_CONTROL, state.wEditorWindow, /HOURGLASS
  813.  
  814.      ; Get the text to be displayed in the text widget
  815.    GetText, state, FILENAME=fileName
  816.  
  817.      ; Get the current window size to be used when the user resizes the window
  818.    WIDGET_CONTROL, wEditorWindow, TLB_GET_SIZE=windowSize
  819.  
  820.      ; Save it in the state structure
  821.    state.windowSize = windowSize
  822.  
  823.      ; Save the state structure in the window's user value
  824.    WIDGET_CONTROL, wEditorWindow, SET_UVALUE=state
  825.  
  826.      ; Register this widget application with the widget manager
  827.    Xmanager, "Editor", wEditorWindow, GROUP_LEADER=group, $
  828.      EVENT_HANDLER="EditorEventHdlr", CLEANUP="EditorKilled", /NO_BLOCK
  829.  
  830. END  ;--------------------- procedure Editor ----------------------------
  831.